package adkr

import (
	"ebbflow/pkg/crypto"
	"fmt"
	"math/big"
)

// DebugVerifyKnowledgeOfDiscreteLog provides detailed debugging information for verification
func DebugVerifyKnowledgeOfDiscreteLog(
	pk *crypto.PaillierPublicKey,
	g crypto.G1,
	Y []byte,
	c *big.Int,
	proof []interface{},
) (bool, string) {
	var debugLog string
	addLog := func(format string, args ...interface{}) {
		debugLog += fmt.Sprintf(format+"\n", args...)
	}

	if IsFastBench() {
		return true, "Fast-bench mode: skipping verification"
	}

	// Handle full format: [Y, c, [T, z, eu, w]] or [Y, c, [T, z, eu, w, zModN]]
	if len(proof) == 3 {
		innerProof, ok := proof[2].([]interface{})
		if !ok || (len(innerProof) != 4 && len(innerProof) != 5) {
			return false, "Invalid full format proof structure"
		}
		return DebugVerifyKnowledgeOfDiscreteLog(pk, g, Y, c, innerProof)
	}

	if len(proof) != 4 && len(proof) != 5 {
		return false, "Invalid proof length"
	}

	TBytes, ok1 := proof[0].([]byte)
	if !ok1 {
		return false, "Failed to extract T"
	}

	var zVal crypto.ZR
	ok2 := false
	if z, ok := proof[1].(crypto.ZR); ok {
		zVal = z
		ok2 = true
	} else if zBigInt, ok := proof[1].(*big.Int); ok {
		var z crypto.ZR
		z.SetBigInt(zBigInt)
		zVal = z
		ok2 = true
	}
	if !ok2 {
		return false, "Failed to extract z"
	}

	var eu *PaillierCiphertext
	ok3 := false
	if euVal, ok := proof[2].(*PaillierCiphertext); ok {
		eu = euVal
		ok3 = true
	} else if euCiphertext, ok := proof[2].(*crypto.PaillierCiphertext); ok {
		eu = &PaillierCiphertext{C: euCiphertext.C}
		ok3 = true
	}
	if !ok3 {
		return false, "Failed to extract eu"
	}

	w, ok4 := proof[3].(*big.Int)
	if !ok4 {
		return false, "Failed to extract w"
	}
	
	// Extract zModN if present (for backward compatibility)
	var zModNFromProof *big.Int
	if len(proof) == 5 {
		if zModNVal, ok := proof[4].(*big.Int); ok {
			zModNFromProof = zModNVal
		}
	}

	// Deserialize T
	T, err := deserializeG1(TBytes)
	if err != nil {
		return false, fmt.Sprintf("Failed to deserialize T: %v", err)
	}

	// Verify T
	e := computeChallenge(pk, &g, Y, c, &T)
	var eZr crypto.ZR
	eZr.SetBigInt(e)
	var negE crypto.ZR
	negE.Neg(&eZr)

	expectedT1 := crypto.MulG1(&g, &zVal)
	YDeserialized := deserializeG1FromBytes(Y)
	if YDeserialized == nil {
		return false, "Failed to deserialize Y"
	}
	expectedT2 := crypto.MulG1(YDeserialized, &negE)
	expectedT := crypto.AddG1(&expectedT1, &expectedT2)

	if !T.G1Affine.Equal(&expectedT.G1Affine) {
		return false, "T verification failed"
	}

	addLog("T verification passed")

	// Check Paillier homomorphic property
	// Use zModN from proof if available, otherwise compute from z
	var zModN *big.Int
	if zModNFromProof != nil {
		// Use zModN from proof (correct value accounting for ZR reduction)
		zModN = zModNFromProof
		addLog("Using zModN from proof: %s", zModN.String())
	} else {
		// Fallback: compute from z (may be incorrect due to ZR reduction)
		zBigInt := ZRToBigInt(&zVal)
		zModN = new(big.Int).Mod(zBigInt, pk.N)
		addLog("z (as big.Int, before mod n): %s", zBigInt.String())
		addLog("z mod n (computed from z): %s", zModN.String())
	}
	addLog("z mod n: %s", zModN.String())
	addLog("w (should be mod n): %s", w.String())
	addLog("w < n: %v", w.Cmp(pk.N) < 0)
	
	// Try to decrypt leftSide and rightSide to see what they decrypt to
	// This requires the private key, which we don't have in the verification function
	// But we can log the values for manual inspection
	addLog("Note: To verify, we need to decrypt leftSide and rightSide")
	addLog("leftSide should decrypt to (u + e*x) mod n")
	addLog("rightSide should decrypt to z mod n")
	addLog("For verification to pass, these must be equal")

	// Compute left side: eu * c^e
	cE := new(big.Int).Exp(c, e, pk.NSquare)
	leftSide := new(big.Int).Mul(eu.C, cE)
	leftSide.Mod(leftSide, pk.NSquare)
	addLog("leftSide (eu * c^e mod n^2): %s", leftSide.String())
	addLog("eu.C: %s", eu.C.String())
	addLog("c: %s", c.String())
	addLog("c^e mod n^2: %s", cE.String())
	addLog("e: %s", e.String())

	// Let's manually compute what eu * c^e should be
	// eu = Enc(u, s) = g^u * s^n mod n^2
	// c = Enc(x, r) = g^x * r^n mod n^2
	// According to Paillier homomorphic property:
	// Enc(u) * Enc(x)^e = Enc(u) * Enc(e*x) = Enc(u + e*x mod n)
	// So leftSide should decrypt to (u + e*x) mod n
	
	// Let's check what leftSide decrypts to
	// We need a private key to decrypt, but we can compute manually:
	// leftSide = eu * c^e mod n^2
	// This should equal Enc(u + e*x mod n, some_randomness)
	
	// Let's verify the homomorphic property manually:
	// eu * c^e = (g^u * s^n) * (g^x * r^n)^e mod n^2
	//          = g^u * s^n * g^(e*x) * r^(e*n) mod n^2
	//          = g^(u + e*x) * s^n * r^(e*n) mod n^2
	//          = g^(u + e*x) * (s * r^e)^n mod n^2 (if multiplication is commutative mod n^2)
	
	// But wait: (s * r^e)^n mod n^2 != s^n * r^(e*n) mod n^2 in general
	// Actually: (a * b)^n = a^n * b^n for commutative operations
	// So: (s * r^e)^n = s^n * (r^e)^n = s^n * r^(e*n) mod n^2
	
	addLog("Checking homomorphic computation manually...")
	
	// Let's decrypt leftSide to see what it decrypts to
	// We need the private key - let's see if we can get it from context
	// For now, let's manually verify the math:
	// eu * c^e should equal Enc(u + e*x mod n, w) where w = r^e * s mod n^2
	
	// According to Paillier homomorphic property:
	// Enc(a, r1) * Enc(b, r2) = Enc(a + b mod n, r1 * r2 mod n^2)
	// But for exponentiation: Enc(a, r)^e = Enc(e*a mod n, r^e mod n^2)
	// So: eu * c^e = Enc(u, s) * Enc(x, r)^e = Enc(u, s) * Enc(e*x mod n, r^e) = Enc(u + e*x mod n, s * r^e mod n^2)
	// But wait, Enc(u, s) * Enc(e*x, r^e) = Enc(u + e*x mod n, s * r^e mod n^2)
	// However, we have w = r^e * s mod n^2, which is the same as s * r^e mod n^2 (multiplication is commutative)
	
	// So leftSide should decrypt to (u + e*x) mod n
	// And rightSide = Enc(z, w) should decrypt to z mod n
	// So we need z mod n == (u + e*x) mod n
	
	addLog("The issue might be that z is computed in ZR (elliptic curve field) but Paillier works mod n")
	addLog("We need to ensure z mod n is used consistently")
	
	// Let's decrypt leftSide and rightSide to see what they decrypt to
	// We need access to the private key - let's check if we can get it
	// Actually, let's manually compute what leftSide should decrypt to:
	// leftSide = eu * c^e = Enc(u, s) * Enc(x, r)^e
	// According to Paillier: Enc(m1, r1) * Enc(m2, r2) = Enc(m1 + m2 mod n, r1 * r2 mod n^2)
	// And: Enc(m, r)^e = Enc(e*m mod n, r^e mod n^2) (when g^e*m = g^(e*m mod n) since g has order n)
	// So: Enc(u, s) * Enc(x, r)^e = Enc(u, s) * Enc(e*x mod n, r^e) = Enc(u + e*x mod n, s * r^e mod n^2)
	// We have w = r^e * s mod n^2, so leftSide should decrypt to (u + e*x) mod n
	
	// The key insight: In Paillier, g has order n, so g^(u + e*x) = g^((u + e*x) mod n)
	// So when we compute eu * c^e, we get:
	// eu * c^e = (g^u * s^n) * (g^x * r^n)^e mod n^2
	//          = g^u * s^n * g^(e*x) * r^(e*n) mod n^2
	//          = g^(u + e*x) * s^n * r^(e*n) mod n^2
	//          = g^((u + e*x) mod n) * s^n * r^(e*n) mod n^2
	//          = g^((u + e*x) mod n) * (s * r^e)^n mod n^2
	//
	// And we have Enc(z, w) = g^z * w^n mod n^2 = g^(z mod n) * w^n mod n^2
	//
	// So for equality: g^((u + e*x) mod n) * (s * r^e)^n = g^(z mod n) * w^n mod n^2
	// Which means: (u + e*x) mod n = z mod n, and (s * r^e) mod n^2 = w mod n^2
	// We have w = r^e * s mod n^2, so (s * r^e) = w, which is correct.
	//
	// The issue might be that when computing g^(u + e*x), we need to use (u + e*x) mod n.
	// But in our current calculation, we're computing g^(u + e*x) directly, which is correct
	// because g^(a) = g^(a mod n) when g has order n.
	//
	// So the problem must be elsewhere. Let's check if the issue is in how we compute w.
	
	// Compute right side: Enc(z mod n, w) = g^(z mod n) * w^n mod n^2
	// w is mod n (we compute it as (s * r^e) mod n)
	gZModN := new(big.Int).Exp(pk.G, zModN, pk.NSquare)
	wN := new(big.Int).Exp(w, pk.N, pk.NSquare)
	rightSide := new(big.Int).Mul(gZModN, wN)
	rightSide.Mod(rightSide, pk.NSquare)
	addLog("rightSide (Enc(z mod n, w)): %s", rightSide.String())
	addLog("leftSide == rightSide: %v", leftSide.Cmp(rightSide) == 0)

	if leftSide.Cmp(rightSide) == 0 {
		return true, debugLog
	}

	return false, debugLog
}

